﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Utils.Logger;
using Nage.Algorithms.Stop;
using System.Threading;
using Nage.Algorithms.Population;
using Nage.Examples.Rastrigin.Solution;
using System.Globalization;

namespace Nage.Examples.Rastrigin.Gui
{
    public partial class MainForm : Form
    {
        private int populationSize = 100;
        private double mutationRate = 0.3;
        private double delta = 0.8;
        private double crossoverRate = 0.8;
        private int stepCount = 2000;

        private List<IPopulation<Coordinates>> coordinateList;
        private Object coordinateListLocker;

        private RastriginProblem problem;
        private Thread problemThread;
        private Boolean solvingStarted = false;

        #region Constructor
        public MainForm()
        {
            SetCulture();
            InitializeComponent();
            InitializeGui();

            coordinateList = new List<IPopulation<Coordinates>>();
            coordinateListLocker = new object();
        } 
        #endregion

        #region SetCulture
        static void SetCulture()
        {
            var culture = new CultureInfo("en-US");
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = culture;
        }
        #endregion

        #region InitializeGui
        private void InitializeGui()
        {
            populationSizeTxt.Text = populationSize.ToString();
            mutationRateTxt.Text = mutationRate.ToString();
            mutationDeltaTxt.Text = delta.ToString();
            crossoverRateTxt.Text = crossoverRate.ToString();
            generationSizeTxt.Text = stepCount.ToString();

            this.FormClosing += new FormClosingEventHandler(OnFormClosing);
        }
        #endregion

        #region OnFormClosing
        void OnFormClosing(object sender, FormClosingEventArgs e)
        {
            StopSolving();
        }  
        #endregion

        #region goBtn_Click
        private void goBtn_Click(object sender, EventArgs e)
        {
            if (!solvingStarted)
            {
                StartSolving();
            }
            else
            {
                StopSolving();
            }
        } 
        #endregion

        #region StopSolving
        private void StopSolving()
        {
            if (problemThread != null) {
                try
                {
                    problemThread.Abort();
                }
                catch (Exception) { }
            }
            parametersBox.Enabled = true;
            solvingStarted = false;
            goBtn.Text = "Go";
        } 
        #endregion

        #region StartSolving
        private void StartSolving()
        {
            if (!ValidateParameters())
            {
                MessageBox.Show("Please, provide valid parameter values!");
                return;
            }

            lock (coordinateListLocker)
            {
                coordinateList.Clear();
            }

            parametersBox.Enabled = false;
            generationTrackBar.Maximum = 1;
            generationTrackBar.Value = 1;
            generationTrackBar.Visible = true;

            problem = new RastriginProblem(populationSize, mutationRate, delta, crossoverRate, stepCount);

            problemThread = new Thread(() =>
            {
                problem.StepCompleted += problem_StepCompleted;
                problem.Solve();
                problem.StepCompleted -= problem_StepCompleted;
                UpdateGuiAfterSolving();
            });
            problemThread.Start();
            goBtn.Text = "Stop";
            solvingStarted = true;
        } 
        #endregion

        #region UpdateGuiAfterSolving
        void UpdateGuiAfterSolving()
        {
            this.BeginInvoke(new Action(() =>
            {
                parametersBox.Enabled = true;
                solvingStarted = false;
                goBtn.Text = "Go";
            }));
        } 
        #endregion

        #region problem_StepCompleted
        void problem_StepCompleted(Algorithms.Population.IPopulation<Coordinates> obj)
        {
            lock (coordinateListLocker)
            {
                coordinateList.Add(obj);
            }

            this.BeginInvoke(new Action(() =>
            {
                FixedStepsStopCondition stopCond = problem.StopCondition as FixedStepsStopCondition;
                if (stopCond.Current <= stopCond.Total)
                {
                    generationTrackBar.Maximum = stopCond.Current;
                    generationLbl.Text = String.Format("{0} / {1}", generationTrackBar.Value, stopCond.Current);
                }
                if (stopCond.Current == 1)
                {
                    generationTrackBar_Scroll(null, null);
                }
            }));
        } 
        #endregion

        #region ValidateParameters
        private Boolean ValidateParameters()
        {
            try
            {
                populationSize = Convert.ToInt32(populationSizeTxt.Text);
                mutationRate = Convert.ToDouble(mutationRateTxt.Text);
                delta = Convert.ToDouble(mutationDeltaTxt.Text);
                crossoverRate = Convert.ToDouble(crossoverRateTxt.Text);
                stepCount = Convert.ToInt32(generationSizeTxt.Text);
                return true;
            }
            catch (Exception ex)
            {
                CLogger.LogError("Validating parameters failed", ex);
                return false;
            }
        } 
        #endregion

        #region generationTrackBar_Scroll
        private void generationTrackBar_Scroll(object sender, EventArgs e)
        {
            int idx = generationTrackBar.Value - 1;
            generationLbl.Text = String.Format("{0} / {1}", generationTrackBar.Value, generationTrackBar.Maximum);

            IPopulation<Coordinates> currentPopulation = null;
            lock (coordinateListLocker)
            {
                String msg = "Not computed yet";
                if (idx < coordinateList.Count)
                {
                    currentPopulation = coordinateList[idx];

                    Coordinates bestFit = currentPopulation.OrderBy(x => x.Fitness).FirstOrDefault();
                    msg = String.Format("Best fit: {0}\r\nFitness:{1}", bestFit, bestFit.Fitness.ToString("0.00000"));
                    msg += "\r\n===========================================\r\n";
                    msg += currentPopulation.ToString();

                }
                coordinatesTxt.Text = msg;
            }

            if (currentPopulation != null)
            {
                Bitmap img = new Bitmap(pictureBox.Width, pictureBox.Height);
                foreach (Coordinates coords in currentPopulation)
                {
                    int x = Convert.ToInt32((coords.X + 5) * 25);
                    int y = Convert.ToInt32((coords.Y + 5) * 25);
                    if (x >= 0 && x < pictureBox.Width &&
                        y >= 0 && y < pictureBox.Height)
                    {
                        img.SetPixel(x, y, Color.Black);
                    }
                }
                pictureBox.Image = img;
            }
        } 
        #endregion


    }
}
